import { type IIdentified } from "~/models/Project/Project";
import { PNode } from "./prototype";
import { PSlotType } from "../slot/type";
import type { Graph } from "../graph";
import { ISlot } from "../slot/instance"
import { oStorage } from "~/models/Project";
import type { IDeserializable } from "~/models/serialize/IDeserializable";
import type { PSlot } from "../slot/prototype";
import type { TypeTemplate } from "~/models/types/type_template";
import { Dzer } from "~/models/serialize";

/**
 * Trainer Graph Instance Node
 * 节点的实例，存储节点的连接和常量参数的输入
 */
export class INode implements IIdentified, IDeserializable {
  id: string = randomName()
  className: string = 'INode'

  /** 槽的Id列表 */
  slotsId: string[] = []

  /** 槽列表 */
  get slots(): ISlot[] {
    return this.slotsId.map(x => oStorage.value!.slots.find(y => x === y.id)!)
  }

  /** 挂载槽 */
  mount(slot: ISlot) {
    if (!this.slotsId.includes(slot.id)) {
      this.slotsId.push(slot.id)
    }
    if (!oStorage.value!.slots.includes(slot)) {
      oStorage.value!.slots.push(slot)
    }
  }

  /** 卸载槽 */
  unmount(slot: ISlot) {
    remove(this.slotsId, x => x === slot.id)
    remove(oStorage.value!.slots, x => x.id === slot.id)
    slot.disconnect()
  }

  parent(): Graph | undefined {
    return oStorage.value!.graphs.find(x => x.nodesId.includes(this.id))
  }

  /** 位置:x */
  public x: number = 0

  /** 位置:y */
  public y: number = 0

  constructor(
    public prototype: PNode = PNode.Process.Start,
    mount = false
  ) {
    const slots = this.prototype.slots.map(slot => new ISlot(slot, mount))
    this.slotsId = slots.map(x => x.id)
    if (mount) {
      oStorage.value!.nodes.push(this)
    }

    /** 如果原型中使用了模板，实例化时进行模板的克隆，避免耦连 */
    const _tempInProto = prototype.typeTemplates
    if (_tempInProto.length) {
      /** 节点上模板的克隆 */
      this.typeTemplates = _tempInProto.map(x => Dzer.clone(x))
    }
    /** 对slot上的模板进行统一 */
    // this.slots.forEach(slot => {
    //   if (slot.typeTemplate !== undefined) {
    //     const sameT = this.typeTemplates.find(x => x.symbol === slot.typeTemplate!.symbol)
    //     if (sameT) {
    //       slot.typeTemplate = sameT
    //     }
    //   }
    // })
  }

  /** 节点使用的模板 */
  typeTemplates: TypeTemplate[] = []

  // 获取 ProcessIn slot，也可能没有
  public getProcessIn(): ISlot | undefined {
    return this.slots.filter(s => {
      const ps = s.prototype
      return ps.name === '_in' && ps.title === '' && ps.direction === 'in' && ps.type === PSlotType.Process
    })[0]
  }

  // 获取 ProcessOut slot，也可能没有
  public getProcessOut(): ISlot | undefined {
    return this.slots.filter(s => {
      const ps = s.prototype
      return ps.name === '_out' && ps.title === '' && ps.direction === 'out' && ps.type === PSlotType.Process
    })[0]
  }

  /**
   * 获取 前一个 节点，也可能没有。
   * 注意！这个函数目前没有考虑If的子流程合并
   * @param parent 
   * @returns 
   */
  public getPreviousNode(parent: Graph): INode | undefined {
    const peerSlot = this.getProcessIn()?.peer
    if (!peerSlot) {
      return undefined
    }
    for (const node of parent.nodes) {
      if (node.slots.includes(peerSlot)) {
        return node
      }
    }
    return undefined
  }

  // 获取 下一个 节点，也可能没有
  public getNextNode(root: Graph): INode | undefined {
    // console.log(`获取${this.prototype.name}的下一个节点`);
    
    const peerSlot = this.getProcessOut()?.peer
    if (!peerSlot) {
      // console.log(`失败`);
      return undefined
    }
    for (const node of root.nodes) {
      if (node.slots.includes(peerSlot)) {
        // console.log(`是${node.prototype.name}节点`);
        return node
      }
    }
    // console.log(`失败`);
    return undefined
  }

  // 获取 数据槽 列表
  public getDataSlots(): ISlot[] {
    return this.slots.filter(s => s.prototype.type === PSlotType.Data)
  }

  // 获取 进数据 槽列表
  public getInDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.direction === 'in')
  }

  // 获取 出数据 槽列表
  public getOutDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.direction === 'out')
  }

  // 获取 必须数据 槽列表
  public getRequiredDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.defaultValue === undefined)
  }

  // 获取 可选数据 槽列表
  public getOptionalDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.defaultValue !== undefined)
  }

  // 获取 可选，进数据槽 列表
  public getOptionalInDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.direction === 'in' && s.prototype.defaultValue !== undefined)
  }

  // 获取 可选，出数据槽 列表
  public getOptionalOutDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.direction === 'out' && s.prototype.defaultValue !== undefined)
  }

  // 获取 必须，进数据槽 列表
  public getRequiredInDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.direction === 'in' && s.prototype.defaultValue === undefined)
  }

  // 获取 必须，出数据槽 列表
  public getRequiredOutDataSlots(): ISlot[] {
    return this.getDataSlots().filter(s => s.prototype.direction === 'out' && s.prototype.defaultValue === undefined)
  }

  // 获取 流程槽 列表
  public getProcessSlots(): ISlot[] {
    return this.slots.filter(s => s.prototype.type === PSlotType.Process)
  }

  // 获取 额外流程槽 列表
  public getExtraProcessSlots(): ISlot[] {
    return this.getProcessSlots().filter(s => s.prototype.name !== '_in' && s.prototype.name !== '_out')
  }

  // 获取 额外进流程槽 列表
  public getExtraInProcessSlots(): ISlot[] {
    return this.getExtraProcessSlots().filter(s => s.prototype.direction === 'in')
  }

  // 获取 额外出流程槽 列表
  public getExtraOutProcessSlots(): ISlot[] {
    return this.getExtraProcessSlots().filter(s => s.prototype.direction === 'out')
  }

  /** 修改原型 */
  update(newProto: PNode) {
    const oldProto = this.prototype
    // 检测不支持的修改
    if (
      oldProto.type !== newProto.type
    ) {
      console.error(oldProto);
      console.error(newProto);
      throw new Error(`不支持的修改`)
    }

    // 应用新原型
    this.prototype = newProto

    /**
     * 对于节点的更新，原有的连接需要保留。
     * 这里采用一种符合直觉的方法。记录旧槽的数据类型和类型名。
     * 在替换槽之后进行优先匹配
     */

    /** 记录peer -> this.slot.protype */
    const oldConnectcion = new Map<ISlot, PSlot>()
    this.slots.forEach(islot => {
      islot.peers.forEach(peer => {
        oldConnectcion.set(peer, islot.prototype)
      })
    })

    // 所有旧的节点卸载
    this.slots.forEach(slot => this.unmount(slot))

    // 创建新的节点
    this.prototype.slots.map(slot => this.mount(new ISlot(slot)))

    // 根据旧的连接，看看还能不能修复
    for (const peer of oldConnectcion.keys()) {
      const wantProto = oldConnectcion.get(peer)!
      let success = false

      // 第1轮：精准匹配
      for (const slot of this.slots) {
        if (slot.prototype.same(wantProto)) {
          peer.connect(slot)
          success = true
          break
        }
      }
      if (success) { continue }

      // 第2轮：类型匹配(被修改了名字)
      for (const slot of this.slots) {
        const p = slot.prototype
        if (slot.prototype.replaceable(wantProto)) {
          peer.connect(slot)
          success = true
          break
        }
      }
      if (success) { continue }
    }
  }
}